extends CharacterBody2D
class_name PlayerController

# 导入伤害文本类
const DamageTextClass = preload("res://scripts/ui/damage_text.gd")

## 移动设置
@export var move_speed: float = 150.0
@export var acceleration: float = 1500.0
@export var friction: float = 1200.0

## 战斗设置
@export var attack_range: float = 50.0
@export var attack_rate: float = 1.8  # 每秒攻击次数（DPS频率）
@export var attack_damage: int = 10

## 子弹设置
@export var bullet_scene: PackedScene

## 生命值设置
@export var max_health: int = 10
@export var invincibility_duration: float = 0.5

# 魂石
signal soul_count_changed(new_value: int)
var soul_count: int = 0

# 修饰节点属性加成（临时）
var modifier_bonus: Dictionary = {}

# 拾取半径与可视化
@export var pickup_radius: float = 20.0
@export var show_pickup_radius: bool = true
@export var pickup_dash_len: float = 6.0
@export var pickup_gap_len: float = 4.0
@export var pickup_dash_speed_deg: float = 180.0
var _pickup_dash_phase: float = 0.0
@export var magnet_radius: float = 120.0

# 等级与经验（每个魂石+1经验）
signal xp_changed(current_xp: int, required_xp: int)
signal level_changed(new_level: int)
var level: int = 0
var xp: int = 0
# 经验公式参数：req(level) = a * (d * (h*level - b))^(1/3) + c
# 最终向下取整
@export var exp_a: float = 9.9
@export var exp_d: float = 9.8
@export var exp_h: float = 3.7
@export var exp_b: float = 19.0
@export var exp_c: float = 60.0

# 基础资源与回复
@export var health_regen_per_sec: float = 0.2
@export var energy_max: int = 50
@export var energy_regen_per_sec: float = 10.0
var energy: float = 0.0
var _health_regen_accum: float = 0.0

## 组件引用
@onready var animation_player: AnimationPlayer = $AnimationPlayer
@onready var sprite: Sprite2D = $Sprite2D
@onready var attack_area: Area2D = $AttackArea

## 私有变量
var input_vector: Vector2 = Vector2.ZERO
var joystick_input: Vector2 = Vector2.ZERO  # 摇杆输入
var is_attacking: bool = false
var last_attack_time: float = 0.0
var current_health: int
var is_invincible: bool = false
var invincibility_timer: float = 0.0
var attack_joystick_direction: Vector2 = Vector2.RIGHT  # 攻击方向

func _ready():
	# 初始化生命值
	current_health = max_health
	
	# 设置攻击区域用于碰撞检测
	if attack_area:
		attack_area.monitoring = true
		attack_area.body_entered.connect(_on_body_entered)
		print("玩家碰撞信号已连接")
	
	# 加载子弹场景
	bullet_scene = preload("res://scenes/bullet.tscn")
	# 确保玩家在组 player 中（供魂石磁吸/拾取）
	if not is_in_group("player"):
		add_to_group("player")
	# 初始化能量为上限
	energy = float(energy_max)

func add_soul_count(amount: int) -> void:
	soul_count += amount
	emit_signal("soul_count_changed", soul_count)
	print("魂石+", amount, " 当前: ", soul_count)
	# 经验
	_add_xp(amount)

func _add_xp(amount: int) -> void:
	xp += max(0, amount)
	var req := get_required_xp_for_level(level)
	while xp >= req:
		xp -= req
		level += 1
		emit_signal("level_changed", level)
		req = get_required_xp_for_level(level)
	emit_signal("xp_changed", xp, req)

func get_required_xp_for_level(lv: int) -> int:
	var inner := exp_d * (exp_h * lv - exp_b)
	# 实数立方根：允许负数 cbrt(-x) = -cbrt(x)
	var cube_root := 0.0
	if inner != 0.0:
		cube_root = sign(inner) * pow(abs(inner), 1.0/3.0)
	var value := exp_a * cube_root + exp_c
	return int(floor(value))

func _physics_process(delta):
	handle_input()
	handle_movement(delta)
	handle_attack()
	update_animation()
	update_invincibility(delta)
	# 生命与能量回复
	_apply_regeneration(delta)
	# 旋转虚线效果
	if show_pickup_radius:
		_pickup_dash_phase = fmod(_pickup_dash_phase + deg_to_rad(pickup_dash_speed_deg) * delta, TAU)
		queue_redraw()

func handle_input():
	input_vector = Vector2.ZERO
	
	# 键盘输入
	if Input.is_action_pressed("move_right"):
		input_vector.x += 1
	if Input.is_action_pressed("move_left"):
		input_vector.x -= 1
	if Input.is_action_pressed("move_down"):
		input_vector.y += 1
	if Input.is_action_pressed("move_up"):
		input_vector.y -= 1
	
	# 摇杆输入（优先级更高）
	if joystick_input.length() > 0.1:  # 摇杆有输入时使用摇杆
		input_vector = joystick_input
	else:
		# 标准化键盘输入向量
		input_vector = input_vector.normalized()

func handle_movement(delta):
	if input_vector != Vector2.ZERO:
		# 加速移动
		velocity = velocity.move_toward(input_vector * move_speed, acceleration * delta)
		
		# 更新朝向
		if input_vector.x != 0:
			sprite.flip_h = input_vector.x < 0
	else:
		# 应用摩擦力
		velocity = velocity.move_toward(Vector2.ZERO, friction * delta)
	
	move_and_slide()

func handle_attack():
	# 键盘攻击（保留作为备用）
	if Input.is_action_just_pressed("attack") and can_attack():
		attack()
	
	# 摇杆攻击（持续攻击）
	# 这个函数会被UI调用来处理摇杆攻击

func can_attack() -> bool:
	var period_ms = 0
	if attack_rate > 0.0:
		period_ms = int(1000.0 / attack_rate)
	else:
		period_ms = 99999999
	return not is_attacking and Time.get_ticks_msec() >= last_attack_time + period_ms

func attack():
	# 根节点触发：让配置图决定由哪些攻击节点开火
	var graph := _get_config_graph()
	var fired := false
	if graph and graph.has_method("perform_attack"):
		var dir := attack_joystick_direction if attack_joystick_direction.length() > 0.1 else Vector2.ZERO
		var res = graph.call("perform_attack", self, dir)
		# 如果对方函数内部包含 await，则立即返回 GDScriptFunctionState，需要等待其完成
		if typeof(res) == TYPE_OBJECT and res.has_method("is_valid" ) and res.has_method("is_completed"):
			res = await res
		if typeof(res) == TYPE_BOOL:
			fired = res
		else:
			fired = (res != null)
	# 若没有有效连接或能量不足，直接返回，不进入冷却
	if not fired:
		print("[Attack] 根节点未连接有效攻击节点或能量不足，跳过攻击。")
		return
	
	# 真正发射时才进入攻击状态并触发冷却
	is_attacking = true
	last_attack_time = Time.get_ticks_msec()
	
	# 播放攻击动画
	if animation_player and animation_player.has_animation("attack"):
		animation_player.play("attack")
	
	# 延迟重置攻击状态
	await get_tree().create_timer(0.3).timeout
	is_attacking = false

func update_animation():
	if not animation_player:
		return
	
	if is_attacking:
		return
	
	if velocity.length() > 10:
		if animation_player.has_animation("walk"):
			animation_player.play("walk")
	else:
		if animation_player.has_animation("idle"):
			animation_player.play("idle")

func update_invincibility(delta):
	if is_invincible:
		invincibility_timer -= delta
		if invincibility_timer <= 0:
			is_invincible = false
			# 恢复正常颜色
			sprite.modulate = Color(0.8, 0.8, 0.8, 1)

func take_damage(damage: int):
	if is_invincible:
		return  # 无敌状态下不受伤害
	
	current_health -= damage
	current_health = max(0, current_health)  # 确保生命值不为负数
	
	# 进入无敌状态
	is_invincible = true
	invincibility_timer = invincibility_duration
	
	# 闪烁效果（无敌状态视觉提示）
	sprite.modulate = Color(1.0, 0.5, 0.5, 0.7)
	
	print("玩家受到伤害: ", damage, " 当前生命值: ", current_health)
	
	# 如果生命值为0，游戏结束
	if current_health <= 0:
		die()

func die():
	print("玩家死亡！")
	# 这里可以添加游戏结束逻辑
	# 比如重新开始游戏或显示游戏结束界面

func _apply_regeneration(delta: float) -> void:
	# 生命回复（按秒率，取整生效）
	if health_regen_per_sec > 0.0 and current_health < max_health:
		_health_regen_accum += health_regen_per_sec * delta
		if _health_regen_accum >= 1.0:
			var inc := int(_health_regen_accum)
			current_health = min(max_health, current_health + inc)
			_health_regen_accum -= float(inc)
	# 能量回复（可为小数）
	if energy_regen_per_sec > 0.0 and energy < float(energy_max):
		energy = min(float(energy_max), energy + energy_regen_per_sec * delta)

func get_health_regen_per_sec() -> float:
	return health_regen_per_sec

func get_energy_regen_per_sec() -> float:
	return energy_regen_per_sec

func _draw() -> void:
	if not show_pickup_radius:
		return
	var r := pickup_radius
	var dash_total := pickup_dash_len + pickup_gap_len
	if dash_total <= 0.1:
		return
	var circumference := TAU * r
	var num_dashes := int(max(6.0, floor(circumference / dash_total)))
	var step := TAU / float(num_dashes)
	var color := Color(1,1,1,0.85)
	for i in range(num_dashes):
		# 相位偏移使虚线旋转
		var a0 := i * step + _pickup_dash_phase
		var a1 := a0 + step * (pickup_dash_len / dash_total)
		var p0 := Vector2.RIGHT.rotated(a0) * r
		var p1 := Vector2.RIGHT.rotated(a1) * r
		draw_line(p0, p1, color, 1.5)

func get_pickup_radius() -> float:
	return pickup_radius

func get_magnet_radius() -> float:
	return magnet_radius

## 碰撞检测 - 用于敌人伤害玩家
func _on_body_entered(body):
	# 排除玩家自己
	if body == self:
		return
		
	# 如果碰撞的是敌人，玩家受到伤害
	if body.has_method("take_damage") and body.is_in_group("enemy"):
		print("玩家被敌人碰撞，受到伤害")
		# 显示伤害数值
		show_damage_text(15, global_position, false)
		take_damage(15)  # 敌人碰撞伤害

## 发射子弹
func shoot_bullet():
	if not bullet_scene:
		return
	
	# 找到最近的敌人
	var nearest_enemy = find_nearest_enemy()
	if not nearest_enemy:
		# 如果没有敌人，向玩家朝向发射（带散布）
		_create_bullets_with_spread(input_vector if input_vector != Vector2.ZERO else Vector2.RIGHT)
		return
	
	# 计算预判目标位置
	var predicted_position = predict_enemy_position(nearest_enemy)
	
	# 计算朝向预测位置的方向
	var direction = (predicted_position - global_position).normalized()
	_create_bullets_with_spread(direction)

## 使用指定方向发射子弹（用于摇杆攻击）
func shoot_bullet_with_direction(direction: Vector2):
	if not bullet_scene:
		return
	
	# 使用摇杆指定的方向（带散布）
	_create_bullets_with_spread(direction)

## 批量按散布创建子弹
func _create_bullets_with_spread(base_direction: Vector2):
	var props = _get_bullet_props()
	var count: int = props["count"]
	var spread_deg: float = props["spread_deg"]
	# 能量结算在图逻辑中完成，这里不再二次扣能
	var base_angle = base_direction.angle()
	
	if count <= 1:
		# 单发也加入轻微随机散布（±spread/2）
		var rand_offset = deg_to_rad(spread_deg) * (randf() - 0.5)
		_create_single_bullet(Vector2.RIGHT.rotated(base_angle + rand_offset))
		return
	
	# 多发按等间距散布，居中对称
	var step = deg_to_rad(spread_deg)
	var mid = (count - 1) / 2.0
	for i in range(count):
		var offset_angle = (i - mid) * step
		var dir = Vector2.RIGHT.rotated(base_angle + offset_angle)
		_create_single_bullet(dir)

func _create_single_bullet(direction: Vector2):
	create_bullet(direction)

## 读取子弹的数量与散布角（度）
func _get_bullet_props() -> Dictionary:
	var count = 1
	var spread_deg = 0.0
	var energy_cost = 0
	if bullet_scene:
		var temp = bullet_scene.instantiate()
		if temp.has_method("get"):
			if temp.get("bullet_count"):
				count = int(temp.get("bullet_count"))
			if temp.get("spread_angle"):
				spread_deg = float(temp.get("spread_angle"))
			if temp.get("energy_cost"):
				energy_cost = int(temp.get("energy_cost"))
		temp.queue_free()
	
	# 应用修饰节点加成
	count += modifier_bonus.get("bullet_count", 0)
	spread_deg += modifier_bonus.get("spread_angle", 0.0)
	energy_cost += modifier_bonus.get("energy_cost", 0)
	
	return {"count": count, "spread_deg": spread_deg, "energy_cost": energy_cost}

## 创建子弹
func create_bullet(direction: Vector2):
	var bullet = bullet_scene.instantiate()
	get_tree().current_scene.add_child(bullet)
	bullet.global_position = global_position
	bullet.set_direction(direction)
	
	# 应用修饰节点属性到子弹
	_apply_modifier_bonus_to_bullet(bullet)
	
	print("发射子弹，方向: ", direction)

# 应用修饰节点属性到子弹
func _apply_modifier_bonus_to_bullet(bullet: Node):
	if not bullet:
		return
	
	# 应用伤害加成
	if bullet.has_method("set") and modifier_bonus.has("damage"):
		var base_damage = bullet.get("damage") if bullet.has_method("get") and bullet.get("damage") != null else 0
		bullet.set("damage", base_damage + modifier_bonus["damage"])
	
	# 应用暴击概率加成
	if bullet.has_method("set") and modifier_bonus.has("crit_chance"):
		var base_crit = bullet.get("crit_rate") if bullet.has_method("get") and bullet.get("crit_rate") != null else 0.0
		bullet.set("crit_rate", base_crit + modifier_bonus["crit_chance"])
	
	# 应用速度加成
	if bullet.has_method("set") and modifier_bonus.has("speed"):
		var base_speed = bullet.get("speed") if bullet.has_method("get") and bullet.get("speed") != null else 0.0
		var new_speed: float = base_speed + modifier_bonus["speed"]
		# 子弹速度下限为 0
		if new_speed < 0.0:
			new_speed = 0.0
		bullet.set("speed", new_speed)
	
	# 应用存在时间加成
	if bullet.has_method("set") and modifier_bonus.has("life_time"):
		var base_life = bullet.get("lifetime") if bullet.has_method("get") and bullet.get("lifetime") != null else 0.0
		bullet.set("lifetime", base_life + modifier_bonus["life_time"])


## 找到最近的敌人
func find_nearest_enemy() -> Node2D:
	var enemies = get_tree().get_nodes_in_group("enemy")
	if enemies.is_empty():
		return null
	
	var nearest_enemy = null
	var nearest_distance = INF
	
	for enemy in enemies:
		var distance = global_position.distance_to(enemy.global_position)
		if distance < nearest_distance:
			nearest_distance = distance
			nearest_enemy = enemy
	
	return nearest_enemy

## 预测敌人未来位置
func predict_enemy_position(enemy: Node2D) -> Vector2:
	if not enemy or not enemy.has_method("get_enemy_velocity"):
		return enemy.global_position
	
	# 获取敌人当前速度
	var enemy_velocity = enemy.get_enemy_velocity()
	
	# 获取子弹速度
	var bullet_speed = 300.0  # 默认子弹速度
	if bullet_scene:
		var temp_bullet = bullet_scene.instantiate()
		if temp_bullet.has_method("get") and temp_bullet.get("speed"):
			bullet_speed = max(0.0, temp_bullet.get("speed"))
		temp_bullet.queue_free()
	
	# 计算距离
	var distance = global_position.distance_to(enemy.global_position)
	
	# 使用迭代方法计算更精确的预判位置
	var predicted_position = enemy.global_position
	var time_to_target = 0.0
	
	# 迭代计算，直到收敛
	for i in range(5):  # 最多迭代5次
		time_to_target = distance / bullet_speed
		predicted_position = enemy.global_position + enemy_velocity * time_to_target
		var new_distance = global_position.distance_to(predicted_position)
		
		# 如果距离变化很小，认为已收敛
		if abs(new_distance - distance) < 1.0:
			break
		distance = new_distance
	
	# 添加一些随机性，避免过于精确的预判
	var random_offset = Vector2(
		randf_range(-15, 15),
		randf_range(-15, 15)
	)
	predicted_position += random_offset
	
	# 限制预判距离，避免预判过远
	var max_prediction_distance = 200.0
	var prediction_distance = global_position.distance_to(predicted_position)
	if prediction_distance > max_prediction_distance:
		var direction = (predicted_position - global_position).normalized()
		predicted_position = global_position + direction * max_prediction_distance
	
	print("敌人当前位置: ", enemy.global_position)
	print("敌人速度: ", enemy_velocity)
	print("预测位置: ", predicted_position)
	print("预判时间: ", time_to_target)
	
	return predicted_position

## 显示伤害文本
func show_damage_text(damage_amount: int, pos: Vector2, is_critical: bool):
	# 创建伤害文本
	var damage_text = DamageTextClass.create_damage_text(damage_amount, pos, is_critical)
	
	# 添加到场景中
	get_tree().current_scene.add_child(damage_text)

## 公共方法
func get_move_input() -> Vector2:
	return input_vector

func is_moving() -> bool:
	return input_vector.length() > 0.1

func get_attack_cooldown_progress() -> float:
	var current_time = Time.get_ticks_msec()
	var time_since_last_attack = current_time - last_attack_time
	var period_ms = (1000.0 / attack_rate) if attack_rate > 0.0 else 99999999.0
	return clamp(time_since_last_attack / period_ms, 0.0, 1.0)

func get_current_health() -> int:
	return current_health

func get_health_percentage() -> float:
	return float(current_health) / float(max_health)

## 摇杆输入设置函数
func set_joystick_input(direction: Vector2):
	joystick_input = direction

func set_attack_joystick_direction(direction: Vector2):
	if direction.length() > 0.1:  # 有方向输入时更新攻击方向
		attack_joystick_direction = direction.normalized()
	else:
		attack_joystick_direction = Vector2.ZERO

# 应用修饰节点属性加成
func apply_modifier_bonus(bonus: Dictionary):
	modifier_bonus = bonus.duplicate()
	print("[Player] 应用修饰节点加成：", modifier_bonus)

# 重置修饰节点属性加成
func reset_modifier_bonus():
	modifier_bonus.clear()
	print("[Player] 重置修饰节点加成")

func get_bullet_spread_info() -> Dictionary:
	# 返回 { count, spread_deg }，供UI用于可视化
	return _get_bullet_props()

func _get_config_graph() -> Node:
	# ConfigPage/Graph
	var ui := get_tree().get_first_node_in_group("ui_root")
	if ui == null:
		ui = get_tree().root.find_child("UI", true, false)
	if ui:
		var cfg := ui.get_node_or_null("ConfigPage")
		if cfg:
			return cfg.get_node_or_null("Graph")
	return null
